/**
 * Created by tdzl2003 on 10/14/16.
 */
import {
  Button, Icon,
  Select, Input, Popover,
  Upload,
} from 'antd';
import {
  RichUtils,
  EditorState,
  Modifier,
  convertToRaw,
  Entity,
} from 'draft-js';
import {Map} from 'immutable';
import styles from './RichEdit.less';
import {setAlign, getAlign} from './align';
import React from 'react';
import {getCurrentLinkUrl, setLinkUrl} from './linkUtils';
import {post} from '../../logics/rpc';

function normalAction(reducer) {
  return (editor, value) => {
    const oldState = editor.getEditorState();
    const newState = reducer(oldState, value);
    if (newState === oldState) {
      setTimeout(()=>editor.focus(), 0);
      return;
    }
    editor.setEditorState(newState, () => {
      setTimeout(()=>editor.focus(), 0);
    });
  }
}

function keyCommandAction(command) {
  return normalAction(state=>RichUtils.handleKeyCommand(state, command) || state);
}

function CommandButton({editor, info, ...others}) {
  const editorState = editor.getEditorState();
  const onClick = info.action && (()=>info.action(editor));
  const type = (info.checkActive && info.checkActive(editorState)) ? 'primary' : 'default';
  const disabled = info.checkDisable && info.checkDisable(editorState);

  // global.currentState = editorState;
  return (
    <Button
      {...others}
      onClick={onClick}
      type={type}
      disabled={!!disabled}
    >
      {info.title}
    </Button>
  );
}

class UploadButton extends React.Component {
  state = {
    uploadInfo: null,
  };
  componentWillMount(){
    this.fetchUploadInfo();
    this.timer = setInterval(this.fetchUploadInfo, 30*60*1000);
  }
  componentWillUnmount() {
    clearInterval(this.timer);
  }
  fetchUploadInfo = async () => {
    const uploadInfo = await post('/admin/upload', {}, {});
    this.setState({uploadInfo});
  };
  uidMap = {};
  beforeUpload = async file => {
    const {editor, info} = this.props;
    const uploadId = info.onPreUpload(editor);
    this.uidMap[file.uid] = uploadId;
  };
  onChange = ({fileList}) => {
    const {editor, info} = this.props;
    fileList.forEach(({status, uid, response}) => {
      if (status === 'done' && this.uidMap[uid]) {
        const uploadId = this.uidMap[uid];
        delete this.uidMap[uid];
        info.onPostUpload(editor, uploadId, response);
      }
    })
  };
  render() {
    const {editor, info, ...others} = this.props;
    const {uploadInfo} = this.state;

    if (!uploadInfo) {
      return (
        <Button
          {...others}
          disabled
        >
          {info.title}
        </Button>
      )
    }
    return (
      <Upload
        action={uploadInfo.url}
        name={uploadInfo.fieldName}
        data={uploadInfo.formData}
        showUploadList={false}
        {...info.uploadProps}
        onChange={this.onChange}
        beforeUpload={this.beforeUpload}
        multiple
      >
        <Button
          {...others}
        >
          {info.title}
        </Button>
      </Upload>
    );
  }
}

class PopoverButton extends React.Component{
  state = {visible: false};
  onVisibleChange = visible => {
    this.setState({visible}, () => {
      if (visible && this.contentRef) {
        this.contentRef.focus();
      }
    });
  }
  onConfirm = value => {
    const {editor, info} = this.props;
    if (info.action){
      info.action(editor, value);
    }
    this.setState({visible: false});
  };
  onContentRefChanged = ref=>{
    this.contentRef = ref;
  };
  render() {
    const {editor, info, ...others} = this.props;
    const editorState = editor.getEditorState();
    const type = (info.checkActive && info.checkActive(editorState)) ? 'primary' : 'default';
    const disabled = info.checkDisable && info.checkDisable(editorState);
    const content = info.renderContent({editor, onConfirm:this.onConfirm, ref: this.onContentRefChanged});
    const {visible} = this.state;

    return (
      <Popover
        {...info.props}
        trigger="click"
        content={content}
        visible={visible}
        onVisibleChange={this.onVisibleChange}
      >
        <Button
          {...others}
          type={type}
          disabled={!!disabled}
        >
          {info.title}
        </Button>
      </Popover>
    );
  }
}

class PromptPopOver extends React.Component {
  state = {
    value: this.props.defaultValue,
  };
  onFocus = () => {
    this.focused = true;
  };
  confirm = ev => {
    const {value} = ev.target;
    const {onConfirm} = this.props;
    this.focused = false;
    onConfirm(value);
  };
  componentWillReceiveProps(newProps) {
    if (!this.focused) {
      this.setState({
        value: newProps.defaultValue,
      })
    }
  }
  focus() {
    this.refs.input.refs.input.focus();
  };
  onChange = ev => {
    const {value} = ev.target;
    console.log('Here', value);
    this.setState({value});
  };
  render () {
    const {onConfirm} = this.props;
    const {value} = this.state;
    return (
      <Input
        ref="input"
        onFocus={this.onFocus}
        onBlur={this.confirm}
        onPressEnter={this.confirm}
        value={value}
        onChange={this.onChange}
        />
    )
  }
}

function CommandSelect({editor, info, ...others}) {
  const value = info.getValue && info.getValue(editor.getEditorState());
  return (
    <Select
      style={{minWidth: 180}}
      optionLabelProp="title"
      value={info.options.filter(v=>v.value === value)[0] && value}
      onChange={info.onChange && (value=>info.onChange(editor, value))}
      >
      {info.options.map(v=>
        <Select.Option
          key={v.value}
          value={v.value}
          title={v.title}
        >
          {v.display || v.title}
          </Select.Option>)
      }
    </Select>
  )
}

const buttons = [
  [
    {
      title: '<',
      action: normalAction(EditorState.undo),
      checkDisable: editorState => editorState.getUndoStack().size === 0,
    },
    {
      title: '>',
      action: normalAction(EditorState.redo),
      checkDisable: editorState => editorState.getRedoStack().size === 0,
    }
  ],
  [
    {
      title: <b>B</b>,
      action: keyCommandAction('bold'),
      checkActive: editorState => editorState.getCurrentInlineStyle().get('BOLD'),
    },
    {
      title: <i>I</i>,
      action: keyCommandAction('italic'),
      checkActive: editorState => editorState.getCurrentInlineStyle().get('ITALIC'),
    },
    {
      title: <u>U</u>,
      action: keyCommandAction('underline'),
      checkActive: editorState => editorState.getCurrentInlineStyle().get('UNDERLINE'),
    },
  ],
  [
    {
      title: 'L',
      action: normalAction(editorState => setAlign(editorState, 'left')),
      checkActive: editorState => getAlign(editorState) === 'left',
    },
    {
      title: 'C',
      action: normalAction(editorState => setAlign(editorState, 'center')),
      checkActive: editorState => getAlign(editorState) === 'center',
    },
    {
      title: 'R',
      action: normalAction(editorState => setAlign(editorState, 'right')),
      checkActive: editorState => getAlign(editorState) === 'right',
    },
  ],
  {
    component: CommandSelect,
    title: '段落格式',
    onChange: normalAction((editorState, blockType) => {
      var selection = editorState.getSelection();
      var startKey = selection.getStartKey();
      var endKey = selection.getEndKey();
      var content = editorState.getCurrentContent();
      var target = selection;

      // Triple-click can lead to a selection that includes offset 0 of the
      // following block. The `SelectionState` for this case is accurate, but
      // we should avoid toggling block type for the trailing block because it
      // is a confusing interaction.
      if (startKey !== endKey && selection.getEndOffset() === 0) {
        var blockBefore = content.getBlockBefore(endKey);
        endKey = blockBefore.getKey();
        target = target.merge({
          anchorKey: startKey,
          anchorOffset: selection.getStartOffset(),
          focusKey: endKey,
          focusOffset: blockBefore.getLength(),
          isBackward: false
        });
      }

      var hasAtomicBlock = content.getBlockMap().skipWhile(function (_, k) {
        return k !== startKey;
      }).reverse().skipWhile(function (_, k) {
        return k !== endKey;
      }).some(function (v) {
        return v.getType() === 'atomic';
      });

      if (hasAtomicBlock) {
        return editorState;
      }

      return EditorState.push(editorState, Modifier.setBlockType(content, target, blockType), 'change-block-type');
    }),
    getValue: RichUtils.getCurrentBlockType,
    options: [
      {
        title: '正文',
        display: <span>正文</span>,
        value: 'unstyled',
      },
      {
        title: '标题1',
        display: <h1>标题1</h1>,
        value: 'header-one',
      },
      {
        title: '标题2',
        display: <h2>标题2</h2>,
        value: 'header-two',
      },
      {
        title: '标题3',
        display: <h3>标题3</h3>,
        value: 'header-three',
      },
      {
        title: '标题4',
        display: <h4>标题4</h4>,
        value: 'header-four',
      },
      {
        title: '标题5',
        display: <h5>标题5</h5>,
        value: 'header-five',
      },
      {
        title: '标题6',
        display: <h6>标题6</h6>,
        value: 'header-six',
      },
    ],
  },
  [
    {
      title: 'UL',
      action: normalAction(editorState => RichUtils.toggleBlockType(editorState, 'unordered-list-item')),
      checkActive: editorState => RichUtils.getCurrentBlockType(editorState) === 'unordered-list-item',
    },
    {
      title: 'IL',
      action: normalAction(editorState => RichUtils.toggleBlockType(editorState, 'ordered-list-item')),
      checkActive: editorState => RichUtils.getCurrentBlockType(editorState) === 'ordered-list-item',
    },
  ],
  {
    title: 'Link',
    component: PopoverButton,
    props: {
      title: '链接地址:',
    },
    action: normalAction((editorState, value)=>setLinkUrl(editorState, value)),
    renderContent: (props)=>
      <PromptPopOver
        defaultValue={getCurrentLinkUrl(props.editor.getEditorState())}
        {...props}
      />,
    checkDisable: editorState => {
      const selection = editorState.getSelection();
      return selection.getStartKey() !== selection.getEndKey() ||
          selection.getStartOffset() === selection.getEndOffset();
    },
  },
  {
    title: 'Debug',
    action: editor => console.log(convertToRaw(editor.getEditorState().getCurrentContent())),
  },
  {
    title: 'Image',
    component: UploadButton,
    onPreUpload: (editor) => {
      const entityKey = Entity.create('IMAGE', 'IMMUTABLE', Map({
      }));
      setTimeout(()=>{
        const editorState = editor.getEditorState();
        const contentState = editorState.getCurrentContent();
        const targetRange = editorState.getSelection();
        const newContentState = Modifier.replaceText(
          contentState,
          targetRange,
          ' ',
          editorState.getCurrentInlineStyle(),
          entityKey
        );
        const newState = EditorState.push(editorState, newContentState, 'insert-characters');
        if (newState === editorState) {
          setTimeout(()=>editor.focus(), 0);
          return;
        }
        editor.setEditorState(newState, () => {
          setTimeout(()=>editor.focus(), 0);
        });
      }, 0);
      return entityKey;
    },
    onPostUpload: (editor, entityKey, {hash: key, w, h}) => {
      const oldData = Entity.get(entityKey);
      Entity.replaceData(entityKey, Map({
        key,
        w, h,
      }));
      editor.focus();
    },
    uploadProps: {
      accept: 'image/*',
    },
  },
];

function renderButton(editor, info, key) {
  const Component = info.component || CommandButton;
  return <Component key={key} editor={editor} info={info} {...info.props}/>;
}

function renderButtonGroup(editor, group, key) {
  if (!Array.isArray(group)) {
    return renderButton(editor, group, key);
  }
  return (
    <Button.Group key={key}>
      {group.map((v, i)=>renderButton(editor, v, i))}
    </Button.Group>
  );
}

export default function ActionList({editor}) {
  return (
    <div className={styles.actionList}>
      {buttons.map((v, i)=>renderButtonGroup(editor, v, i))}
    </div>
  );
}
